﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Luminal
{
    public enum EventType : uint
    {
        NDRangeKernel = 0x11F0,
        Task = 0x11F1,
        NativeKernel = 0x11F2,
        ReadBuffer = 0x11F3,
        WriteBuffer = 0x11F4,
        CopyBuffer = 0x11F5,
        ReadImage = 0x11F6,
        WriteImage = 0x11F7,
        CopyImage = 0x11F8,
        CopyImageToBuffer = 0x11F9,
        CopyBufferToImage = 0x11FA,
        MapBuffer = 0x11FB,
        MapImage = 0x11FC,
        UnmapMemoryObject = 0x11FD,
        Marker = 0x11FE,
        //CL_COMMAND_ACQUIRE_GL_OBJECTS = 0x11FF,
        //CL_COMMAND_RELEASE_GL_OBJECTS = 0x1200,
    }

    public enum EventExecutionStatus : uint
    {
        Complete = 0x0,
        Running = 0x1,
        Submitted = 0x2,
        Queued = 0x3,
    }

    public sealed class Event : LuminalObject
    {
        #region Interop
        [DllImport("OpenCL.dll")]
        static extern ErrorCode clGetEventInfo(IntPtr @event,
            cl_event_info param_name,
            SizeT param_value_size,
            IntPtr param_value,
            out SizeT param_value_size_ret);

        [DllImport("OpenCL.dll")]
        static extern ErrorCode clReleaseEvent(IntPtr @event);
        #endregion

        #region Properties
        public EventType Type { get; private set; }
        public EventExecutionStatus Status
        {
            get
            {
                return (EventExecutionStatus)GetInfo<uint>(cl_event_info.CL_EVENT_COMMAND_EXECUTION_STATUS);
            }
        }

        private T GetInfo<T>(cl_event_info info) where T : struct
        {
            SizeT infoSize = 0;
            Util.ThrowError("Could not get event property.", clGetEventInfo(InternalPointer, info, 0, IntPtr.Zero, out infoSize));

            T output = default(T);
            Util.Pin(ref output, ptr =>
            {
                Util.ThrowError("Could not get event property.", clGetEventInfo(InternalPointer, info, infoSize, ptr, out infoSize));
            });

            return output;
        }
        #endregion

        #region Constructor
        private Event(IntPtr ptr)
        {
            Initialize(ptr);

            try
            {
                Type = (EventType)GetInfo<uint>(cl_event_info.CL_EVENT_COMMAND_TYPE);
            }
            catch (Exception ex)
            {
                Dispose();
                throw ex;
            }
        }
        #endregion

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                clReleaseEvent(InternalPointer);
            }

            base.Dispose(disposing);
        }

        public static Event FromHandle(IntPtr ptr)
        {
            return ObjectTracker.ContainsObject(ptr) ? ObjectTracker.GetObject<Event>(ptr) : new Event(ptr);
        }
    }
}
